package com.boot2.core.executor;

import com.boot2.core.HlAssert;
import com.boot2.core.concurrent.RunnableTask;
import com.boot2.core.utils.BeanUtils;
import com.boot2.core.utils.CheckUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * @author zhangweilin
 * @Description: xxxxx
 * @date 2020/12/15
 */
@Slf4j
public class UpdatableScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {

    private Map<String, RunnableTask> runnableTaskMap = new ConcurrentHashMap<>();

    private Map<String, Future<?>> scheduledFutureMap = new ConcurrentHashMap<>();


    public UpdatableScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize);
    }

    public UpdatableScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
        super(corePoolSize, threadFactory);
    }

    public UpdatableScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
        super(corePoolSize, handler);
    }

    public UpdatableScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, threadFactory, handler);
    }


    private void showThreadPoolInfo(String prefix) {

        if (log.isDebugEnabled()) {
            log.debug("  {},taskCount [{}], completedTaskCount [{}], activeCount [{}], poolSize [{}], corePoolSize [{}],largestPoolSize [{}],maximumPoolSize [{}], queueSize [{}]",
                    prefix,
                    getTaskCount(),
                    getCompletedTaskCount(),
                    getActiveCount(),
                    getPoolSize(),
                    getCorePoolSize(),
                    getLargestPoolSize(),
                    getMaximumPoolSize(),
                    getQueue().size());
        }
    }

    public UpdatableScheduledThreadPoolExecutor.ThreadPoolExecutorVo getThreadPoolExecutorVo() {

        UpdatableScheduledThreadPoolExecutor.ThreadPoolExecutorVo threadPoolExecutorVo = new UpdatableScheduledThreadPoolExecutor.ThreadPoolExecutorVo();
        threadPoolExecutorVo.setTaskCount(getTaskCount());
        threadPoolExecutorVo.setCompletedTaskCount(getCompletedTaskCount());
        threadPoolExecutorVo.setActiveCount(getActiveCount());
        threadPoolExecutorVo.setPoolSize(getPoolSize());
        threadPoolExecutorVo.setCorePoolSize(getCorePoolSize());
        threadPoolExecutorVo.setLargestPoolSize(getLargestPoolSize());
        threadPoolExecutorVo.setMaximumPoolSize(getMaximumPoolSize());
        threadPoolExecutorVo.setQueueSize(getQueue().size());
        return threadPoolExecutorVo;
    }

    @Data
    static class ThreadPoolExecutorVo {
        private long taskCount;
        private long completedTaskCount;
        private long activeCount;
        private long poolSize;
        private long corePoolSize;
        private long largestPoolSize;
        private long maximumPoolSize;
        private long queueSize;
    }


    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute: ");
        super.execute(task);
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
        RunnableTask runnableTask = addRunnableMap(command);
        if (runnableTask != null) {
            showThreadPoolInfo("2. do scheduledFuture: ");
            ScheduledFuture<?> scheduledFuture = super.schedule(runnableTask, delay, unit);
            scheduledFutureMap.put(runnableTask.getTaskKey(), scheduledFuture);
            return scheduledFuture;
        }
        return null;
    }

    /**
     * 如果是现存，则只仅仅是更新任务内部属性，不需要重新调度
     *
     * @param command
     * @return
     */
    private RunnableTask addRunnableMap(Runnable command) {
        HlAssert.isTrue((command instanceof RunnableTask), "该线程池只能提交com.boot2.core.concurrent.RunnableTask任务");
        RunnableTask runnableTask = (RunnableTask) command;
        String taskKey = runnableTask.getTaskKey();
        synchronized (taskKey) {
            RunnableTask runnableTask1 = runnableTaskMap.get(taskKey);
            Future<?> future = scheduledFutureMap.get(taskKey);
            //正在运行，未取消，未完成，则更新，还则都添加新的
            if (null != runnableTask1&&future!=null&&!future.isCancelled()&&!future.isDone()) {
                BeanUtils.copyProperties(runnableTask, runnableTask1);
                runnableTask = null;
                return null;
            } else {
                runnableTaskMap.put(taskKey, runnableTask);
                return runnableTask;
            }
        }
    }

    @Override
    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
        showThreadPoolInfo("3. do schedule: ");
        return super.schedule(callable, delay, unit);
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
        RunnableTask runnableTask = addRunnableMap(command);
        if (runnableTask != null) {
            showThreadPoolInfo("4. do scheduleAtFixedRate: ");
            ScheduledFuture<?> scheduledFuture = super.scheduleAtFixedRate(command, initialDelay, period, unit);
            scheduledFutureMap.put(runnableTask.getTaskKey(), scheduledFuture);
            return scheduledFuture;
        }
        return null;
    }

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
        RunnableTask runnableTask = addRunnableMap(command);
        if (runnableTask != null) {
            showThreadPoolInfo("5. do scheduleWithFixedDelay: ");
            ScheduledFuture<?> scheduledFuture = super.scheduleWithFixedDelay(command, initialDelay, delay, unit);
            scheduledFutureMap.put(runnableTask.getTaskKey(), scheduledFuture);
            return scheduledFuture;
        }
        return null;
    }

    @Override
    public <T> Future<T> submit(Runnable task, T result) {
        RunnableTask runnableTask = addRunnableMap(task);
        if (runnableTask != null) {
            showThreadPoolInfo("6. do submit: ");
            Future<T> future = super.submit(task, result);
            scheduledFutureMap.put(runnableTask.getTaskKey(), future);
            return future;
        }
        return null;
    }

    @Override
    public Future<?> submit(Runnable task) {
        RunnableTask runnableTask = addRunnableMap(task);
        if (runnableTask != null) {
            showThreadPoolInfo("7. do submit: ");
            Future<?> future = super.submit(task);
            scheduledFutureMap.put(runnableTask.getTaskKey(), future);
            return future;
        }
        return null;
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("8. do submit: ");
        return super.submit(task);
    }

    public Map<String, RunnableTask> getRunnableTaskMap() {
        return runnableTaskMap;
    }


    @Override
    public boolean remove(Runnable task) {
        if (CheckUtil.isNotEmpty(runnableTaskMap)) {
            runnableTaskMap.remove(task);
        }
        return super.remove(task);
    }
}
